package com.jason.common.video;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.avutil.AVRational;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;

import java.text.MessageFormat;

/**
 * TODO
 *
 * @author gzc
 * @since 2024/1/4 4:48
 **/
@Slf4j
public class Test2 {
    public static void main(String[] args) throws Exception {
        String inputVideoFilePath = "E:\\javacv-test\\JUQ-123-cut.mp4";
        String outputVideoFilePath = "E:\\javacv-test\\JUQ-123-cut111.mp4";
//        FFmpegLogCallback.set();
        videoConvert(inputVideoFilePath, outputVideoFilePath, MessageFormat.format(
                "crop={0}:{1}:{2}:{3}",
                String.valueOf(1280),
                String.valueOf(360),
                String.valueOf(0),
                String.valueOf(0)), 1280, 720, "mp4"
        );
    }

    /**
     * 视频转分辨率转视频编码
     *
     * @param inputFile     源文件
     * @param outputPath    输出目录
     * @param filterContent 过滤器配置参数
     * @param width         需要转成的视频的宽度
     * @param height        需要转成的视频的高度
     * @param videoFormat   需要转换成的视频格式
     * @return 返回新文件名称，可以根据实际使用修改
     */
    public static String videoConvert(String inputFile, String outputPath, String filterContent, Integer width, Integer height, String videoFormat) {
        if (StrUtil.isBlank(inputFile)) {
            videoFormat = "mp4";
        }

        Frame frame;
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
        FFmpegFrameRecorder recorder = null;
        double aspectRatio = 1.0;

        FFmpegFrameFilter filter = new FFmpegFrameFilter(filterContent, grabber.getImageWidth(), grabber.getImageHeight());
        try {

            // 初始化帧抓取器，例如数据结构（时间戳、编码器上下文、帧对象等），
            // 如果入参等于true，还会调用avformat_find_stream_info方法获取流的信息，放入AVFormatContext类型的成员变量oc中
            grabber.start(true);
            // grabber.start方法中，初始化的解码器信息存在放在grabber的成员变量oc中

            //过滤器启动
            filter.start();

            AVFormatContext avformatcontext = grabber.getFormatContext();
            // 文件内有几个媒体流（一般是视频流+音频流）
            int streamNum = avformatcontext.nb_streams();
            // 没有媒体流就不用继续了
            if (streamNum < 1) {
                log.info("视频文件格式不对");
                return "error";
            }
            // 取得视频的帧率
            int framerate = (int) grabber.getVideoFrameRate();
            log.info("视频帧率:{}，视频时长:{}秒，媒体流数量:{}", framerate, avformatcontext.duration() / 1000000,
                    streamNum);
            // 遍历每一个流，检查其类型
            for (int i = 0; i < streamNum; i++) {
                AVStream avstream = avformatcontext.streams(i);
                AVCodecParameters avcodecparameters = avstream.codecpar();
                log.info("流的索引:{}，编码器类型:{}，编码器ID:{}", i, avcodecparameters.codec_type(),
                        avcodecparameters.codec_id());
            }
            // 源视频宽度
            int frameWidth = grabber.getImageWidth();
            // 源视频高度
            int frameHeight = grabber.getImageHeight();
            // 源音频通道数量
            int audioChannels = grabber.getAudioChannels();
            log.info("源视频宽度:{}，源视频高度:{}，音频通道数:{}", frameWidth, frameHeight, audioChannels);


            AVFormatContext ifmt_ctx = grabber.getFormatContext();
            AVStream inpVideoStream = null,
                    inpAudioStream = null;
            if (ifmt_ctx != null) {
                //获取Video的AVStream
                inpVideoStream = ifmt_ctx.streams(grabber.getVideoStream());
                //不能使用 inpVideoStream.sample_aspect_ratio()
                AVRational SAR = inpVideoStream.codecpar().sample_aspect_ratio();
                double a = (double) SAR.num() / (double) SAR.den();
                //默认情况为1:1
                aspectRatio = a == 0.0 ? 1.0 : a;
                int SARden = SAR.den();
                int SARnum = SAR.num();
                log.info("SARden[{}], SARnum[{}]", SARden, SARnum);
            }

            recorder = new FFmpegFrameRecorder(outputPath, width, height, audioChannels);
            // 设置H264编码
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            // 设置视频格式
            recorder.setFormat(videoFormat);
            // 使用原始视频的码率，若需要则自行修改码率
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            // 一秒内的帧数，帧率
            recorder.setFrameRate(framerate);
            // 两个关键帧之间的帧数
            recorder.setGopSize(framerate);
            // 设置音频通道数，与视频源的通道数相等
            recorder.setAudioChannels(grabber.getAudioChannels());

            //给录制器设置SAR
            recorder.setAspectRatio(aspectRatio);

            recorder.start();
            int videoFrameNum = 0;
            int audioFrameNum = 0;
            int dataFrameNum = 0;
            // 持续从视频源取帧
            while (null != (frame = grabber.grab())) {
                // 有图像，就把视频帧加一
                if (null != frame.image) {
                    videoFrameNum++;
//                    //把已经解码后的视频图像像素塞进过滤器
//                    filter.push(frame);
//                    //取出过滤器处理后的图像像素数据
//                    Frame filterFrame = filter.pullImage();
//                    // 取出的每一帧，都保存到视频
//                    recorder.record(filterFrame);
                    recorder.record(frame);
                }
                // 有声音，就把音频帧加一
                if (null != frame.samples) {
                    audioFrameNum++;
                    // 取出的每一帧
                    recorder.record(frame);
                }
                // 有数据，就把数据帧加一
                if (null != frame.data) {
                    dataFrameNum++;
                }
            }
            log.info("转码完成，视频帧:{}，音频帧:{}，数据帧:{}", videoFrameNum, audioFrameNum, dataFrameNum);
            System.out.println(grabber.getTimestamp());
            System.out.println(grabber.getLengthInFrames());
            System.out.println(grabber.getLengthInVideoFrames());
            System.out.println(grabber.getLengthInAudioFrames());
        } catch (Exception e) {
            log.error(e.getMessage());
            return "error";
        } finally {
            if (recorder != null) {
                try {
                    recorder.close();
                } catch (Exception e) {
                    log.info("recorder.close异常" + e);
                }
            }
            try {
                filter.close();
            } catch (FrameFilter.Exception e) {
                log.info("filter.close异常" + e);
            }
            try {
                grabber.close();
            } catch (FrameGrabber.Exception e) {
                log.info("frameGrabber.close异常" + e);
            }

            return "success";
        }
    }

}
